using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Xml;

namespace Universe.Farm.Service
{
    public class ConfigParser
    {
        private XmlElement _XmlConfig;
        private Configuration _config;
        object _syncConfig = new object();


        public ConfigParser(XmlElement doc)
        {
            _XmlConfig = doc;
        }

        Configuration GetConfig()
        {
            Service.Configuration ret = new Configuration();

            foreach (XmlElement el1 in _XmlConfig.SelectNodes("//Listeners/Listener"))
            {
                string typeName = el1.GetAttribute("type");
                string rawId = el1.GetAttribute("id");
                string rawInterval = el1.GetAttribute("interval");

                int interval;
                if (!int.TryParse(rawInterval, out interval))
                    interval = -1;

                XmlElement copy = (XmlElement) el1.Clone();
                copy.Attributes.RemoveNamedItem("type");
                copy.Attributes.RemoveNamedItem("id");
                copy.Attributes.RemoveNamedItem("interval");
                Configuration.Listener lc = new Configuration.Listener(
                    typeName,
                    interval,
                    rawId,
                    copy);

                ret.Listeners.Add(lc);
            }

            XmlElement comFarm = (XmlElement) _XmlConfig.SelectSingleNode("Communication/Farm");
            if (comFarm != null)
            {
                string rawPort = comFarm.GetAttribute("port");
                string rawIgnore = comFarm.GetAttribute("ignore");
                string rawBroadcast = comFarm.GetAttribute("broadcast");
                string rawGroup = comFarm.GetAttribute("group");
                string rawForce = comFarm.GetAttribute("force");

                ret.Communication.PeerList = TryParseAddress(rawForce);
                ret.Communication.IgnoreList = TryParseAddress(rawIgnore);
                ret.Communication.IsBroadcast =
                    string.IsNullOrEmpty(rawBroadcast)
                    || StringComparer.InvariantCultureIgnoreCase.Compare(rawBroadcast, "on") == 0
                    || StringComparer.InvariantCultureIgnoreCase.Compare(rawBroadcast, "true") == 0;

                List<IPAddress> groups = TryParseAddress(rawGroup);
                if (groups.Count > 0)
                    ret.Communication.Group = groups[0];

                int port;
                if (!int.TryParse(rawPort, out port))
                    port = 50505;

                ret.Communication.Port = port;
            }
            else
            {
                ret.Communication.IsBroadcast = true;
                ret.Communication.Port = 50505;
            }

            XmlElement comService = (XmlElement) _XmlConfig.SelectSingleNode("Communication/Service");
            if (comService != null)
            {
                string rawPort = comFarm.GetAttribute("port");
                int port;
                if (!int.TryParse(rawPort, out port))
                    port = 50505;

                ret.Communication.ServicePort = port;
                string rawIp = comFarm.GetAttribute("ip");
                ret.Communication.ServiceAddress = TryParseAddress(rawIp);
            }

            if (ret.Communication.ServicePort == 0)
                ret.Communication.Port = Configuration.CommunicationParameters.DefaultPort;

            if (ret.Communication.ServiceAddress == null)
                ret.Communication.ServiceAddress = new List<IPAddress>();

            if (ret.Communication.ServiceAddress.Count == 0)
                ret.Communication.ServiceAddress.Add(IPAddress.Any);

            return ret;
        }

        static List<IPAddress> TryParseAddress(string raw)
        {
            List<IPAddress> ret = new List<IPAddress>();
            string[] arr = raw.Split(new char[] {';', ','});
            foreach (string s in arr)
            {
                string s2 = s.Trim();
                if (s2.Length > 0)
                {
                    IPAddress addr = IPAddress.None;
                    if (s2.ToLower() == "any")
                        addr = IPAddress.Any;
                    else if (s2.ToLower() == "loopback" || s2 == "127.0.0.1")
                        addr = IPAddress.Loopback;
                    else
                        addr = IPAddress.Parse(s2);

                    ret.Add(addr);
                }
            }

            return ret;
        }

        public Configuration Configuration
        {
            get
            {
                if (_config == null)
                    lock(_syncConfig)
                        if (_config == null)
                            _config = GetConfig();

                return _config;
            }
        }
    }
}
